package be.donovan.model.persist;



import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.util.ReflectionUtils;

import be.donovan.model.Catalog;

public class JpaPersistenceHelper extends JpaDaoSupport implements PersistenceHelper {

	public void delete(Object object) {
		getJpaTemplate().remove(object);
	}

	public void execute(JpaCallback cb) {
		getJpaTemplate().execute(cb);
	}

	public <T> T find(Class<T> persistent, Serializable id) {
		return getJpaTemplate().find(persistent, id);
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> find(Class<T> persistent, String property, Object propertyValue) {
		Map<String, Object> propMap = new HashMap<String, Object>();
		propMap.put(property, propertyValue);
		return (List<T>)find(new WhereJpaCallBack(persistent, propMap, false));
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> find(Class<T> persistent, Map<String, Object> propertyMap) {
		Map<String, Object> propMap = new HashMap<String, Object>();
		for (String property : propertyMap.keySet()) {
			propMap.put(property, propertyMap.get(property));
		}
		return (List<T>)find(new WhereJpaCallBack(persistent, propMap, false));
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> find(JpaCallback findcb) {
		return getJpaTemplate().executeFind(findcb);
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> findAll(Class<T> persistent) {
		return (List<T>)find(new FindallJpaCallBack(persistent));
	}

	@SuppressWarnings("unchecked")
	public <T> T findUnique(Class<T> persistent, String property, Object propertyValue) {
		Map<String, Object> propMap = new HashMap<String, Object>();
		propMap.put(property, propertyValue);
		return (T)findUnique(new WhereJpaCallBack(persistent, propMap, true));
	}

	@SuppressWarnings("unchecked")
	public <T> T findUnique(Class<T> persistent, Map<String, Object> propertyMap) {
		Map<String, Object> propMap = new HashMap<String, Object>();
		for (String property : propertyMap.keySet()) {
			propMap.put(property, propertyMap.get(property));
		}
		return (T)findUnique(new WhereJpaCallBack(persistent, propMap, true));
	}

	@SuppressWarnings("unchecked")
	public <T> T findUnique(JpaCallback findcb) {
		return (T)getJpaTemplate().execute(findcb);
	}

	public void save(Object object) {
		getJpaTemplate().persist(object);
		getJpaTemplate().flush();
	}

	public void update(Object object) {
		getJpaTemplate().merge(object);
	}

	public void refresh(Object object) {
		getJpaTemplate().refresh(object);
		
	}

	protected class WhereJpaCallBack implements JpaCallback {
		
		private final String TABLE = "TABLE ";
		private final String WHERE = "WHERE ";
		private final String ALIAS = " c ";
		private final String SELECT = "SELECT" + ALIAS + "FROM " + TABLE + ALIAS + WHERE;
		private final String AND = " AND ";
		private final String EQUALS = " like ";
		
		private Class persistent;
		private Map<String, Object> properties;
		private boolean uniqueResult;

		public WhereJpaCallBack(Class persistent, Map<String, Object> properties, boolean uniqueResult) {
			super();
			this.persistent = persistent;
			this.properties = properties;
			this.uniqueResult = uniqueResult;
		}

//		public Object doInJpa(EntityManager em) throws PersistenceException {
//			String queryString = new String(SELECT);
//			String whereClause = properties.toString();
//			whereClause = whereClause.replace("{", "c.");
//			whereClause = whereClause.replace('}', ' ');
//			whereClause = whereClause.replaceAll(",", AND + "c.");
//			queryString = queryString.replace(TABLE, persistent.getSimpleName());
//			queryString = queryString + whereClause;
//			if (properties.isEmpty()) queryString = queryString.replace(WHERE, ""); 
//			Query query = em.createQuery(queryString);
//			return (uniqueResult) ? query.getSingleResult() : query.getResultList();
//		}
		public Object doInJpa(EntityManager em) throws PersistenceException {
			String queryString = new String(SELECT);
			StringBuilder whereClause = new StringBuilder();
			for (String propKey : properties.keySet()) {
				whereClause.append(propKey).append(EQUALS).append(":").append(propKey).append(AND);
			}
			queryString = queryString.replace(TABLE, persistent.getSimpleName());
			queryString = queryString + whereClause.substring(0, whereClause.length() - AND.length());
			if (properties.isEmpty()) queryString = queryString.replace(WHERE, ""); 
			Query query = em.createQuery(queryString);
			for (String propKey : properties.keySet()) {
				query.setParameter(propKey, "%"+properties.get(propKey)+'%');
			}
			return (uniqueResult) ? query.getSingleResult() : query.getResultList();
		}
	}
	
	protected class FindallJpaCallBack implements JpaCallback {
		
		private final String TABLE = "TABLE ";
		private final String SELECT = "SELECT c FROM " + TABLE + " c ";
		
		private Class persistent;

		public FindallJpaCallBack(Class persistent) {
			super();
			this.persistent = persistent;
		}
		
		public Object doInJpa(EntityManager em) throws PersistenceException {
			String queryString = new String(SELECT);
			queryString = queryString.replace(TABLE, persistent.getSimpleName());
			Query query = em.createQuery(queryString);
			return query.getResultList();
		}
	}


	public <T> void saveAll(Collection<T> dataVector) {
		for (T t : dataVector) {
			try {
				Field field = ReflectionUtils.findField(t.getClass(), "id", Long.class);
				ReflectionUtils.makeAccessible(field);
				if(field.get(t)!=null){
					this.update(t);
				}else{
					this.save(t);
				}
			} catch (Exception e) {
				ReflectionUtils.handleReflectionException(e);
			} 
		}
	}


	public Collection<Catalog> findCatalogs(Class<Catalog> clazz,
			String text) {
		Map<String, Object> props = new HashMap<String, Object>();
		props.put("description", text);
		return find(new WhereJpaCallBack(clazz, props, false));
	}

}
